import { getBusinessObject, is, isAny } from 'bpmn-js/lib/util/ModelUtil'
import { sortBy, forEach, map, keys, groupBy, flatten } from 'min-dash'
import { getVariablesForScope } from '@bpmn-io/extract-process-variables'

export async function getProcessVariablesList(element) {
  const businessObject = getBusinessObject(element)
  const rootElement = getRootElement(businessObject)
  const scope = getScope(element)
  const rawVariables = await getVariablesForScope(scope, rootElement)
  const withName = populateElementNames(sortByName(rawVariables))
  const byScope = groupByScope(withName)
  const multiScope = isMultiScope(byScope)
  let variableItems = []

  // (2) get variables to display
  if (multiScope) {
    // (2a) multiple scopes, sub scopes first
    // assumption: variables extractor fetches parent variables first
    const reversed = map(reverse(keys(byScope)), (scopeKey) => byScope[scopeKey])
    variableItems = flatten(reversed)
  } else {
    // (2b) single scope
    variableItems = withName
  }
  console.log(variableItems, 'variableItems')
  return variableItems
}

export function isProcessVariablesSupported(element) {
  return canHaveProcessVariables(element)
}

function canHaveProcessVariables(element) {
  const businessObject = getBusinessObject(element)
  return isAny(element, ['bpmn:Process', 'bpmn:SubProcess']) || (is(element, 'bpmn:Participant') && businessObject.get('processRef'))
}

function getRootElement(element) {
  const businessObject = getBusinessObject(element)
  if (is(businessObject, 'bpmn:Participant')) {
    return businessObject.processRef
  }
  if (is(businessObject, 'bpmn:Process')) {
    return businessObject
  }
  let parent = businessObject
  while (parent.$parent && !is(parent, 'bpmn:Process')) {
    parent = parent.$parent
  }
  return parent
}

function getScope(element) {
  const bo = getBusinessObject(element)
  if (is(element, 'bpmn:Participant')) {
    return bo.processRef.id
  }
  return bo.id
}

function sortByName(variables) {
  return sortBy(variables, function (variable: any) {
    return variable.name
  })
}

function groupByScope(variables) {
  return groupBy(variables, 'scope')
}

function populateElementNames(variables) {
  forEach(variables, function (variable: any) {
    const names = map(variable.origin, function (element: any) {
      return element.name || element.id
    })
    variable.origin = names
    variable.scope = variable.scope.name || variable.scope.id
  })
  return variables
}

function isMultiScope(scopedVariables) {
  return keys(scopedVariables).length > 1
}

function reverse(array) {
  return map(array, function (a, i) {
    return array[array.length - 1 - i]
  })
}
